package com.yifeng.repo.apm.endpoint.logging.log4j;


import com.yifeng.repo.apm.endpoint.console.api.model.LogConfTypeEnum;
import com.yifeng.repo.apm.endpoint.sender.LogSenderManager;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.spi.LoggerRepository;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Properties;

/**
 * Created by daibing on 2020/9/3.
 * -Dlog4j.configuratorClass=com.yifeng.repo.apm.endpoint.logging.log4j.TracePropertyConfigurator
 * -Dlogging.config=classpath:log4j.properties
 */
public class TracePropertyConfigurator extends PropertyConfigurator {
    private static final String CONVERSION_PATTERN = "conversionPattern";
    private static final String FILE_CONVERSION_PATTERN = "fileConversionPattern";
    private static final String ROLLING_FILE_APPENDER = "RollingFileAppender";
    private static final String DAILY_ROLLING_FILE_APPENDER = "DailyRollingFileAppender";
    private static final String CLOUD_APPENDER = "CloudAppender";
    private static final String SMART_APPENDER = "SmartAppender";
    private static final String SPINUS_LOGX_APPENDER = "SpinusLogxAppender";
    private static final String ROOT_LOGGER = "log4j.rootLogger";
    private static final String LOG4J_APPENDER_SPINUS = "log4j.appender.spinus";

    @Override
    public void doConfigure(URL configURL, LoggerRepository hierarchy) {
        Properties props = new Properties();
        LogLog.debug("Reading configuration from URL " + configURL);
        InputStream istream = null;
        URLConnection uConn = null;
        try {
            uConn = configURL.openConnection();
            uConn.setUseCaches(false);
            istream = uConn.getInputStream();
            props.load(istream);
        } catch (Exception e) {
            if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            LogLog.error("Could not read configuration file from URL [" + configURL + "].", e);
            LogLog.error("Ignoring configuration file [" + configURL + "].");
            return;
        } finally {
            if (istream != null) {
                try {
                    istream.close();
                } catch (InterruptedIOException ignore) {
                    Thread.currentThread().interrupt();
                } catch (IOException ignore) {
                } catch (RuntimeException ignore) {
                }
            }
        }

        mergeComponentProperties(props);

        doConfigure(props, hierarchy);
    }

    private void mergeComponentProperties(Properties props) {
        LogSenderManager.startupLogSender();
        boolean fileLogEnabled = LogSenderManager.getLogSender(true).isLogEnabled(LogConfTypeEnum.FILE);
        Properties componentProperties = getComponentProperties();
        Enumeration<?> e = props.propertyNames();
        while (e.hasMoreElements()) {
            String originalKey = (String) e.nextElement();
            String propertyValue = props.getProperty(originalKey);
            if (propertyValue.contains(CLOUD_APPENDER)) {
                LogLog.warn(String.format("log4j conf changed: Key=%s, value=%s >>> %s",
                        originalKey, propertyValue, SpinusLogxAppender.class.getName()));
                props.setProperty(originalKey, SpinusLogxAppender.class.getName());
            }
            int lastIndexOf = originalKey.lastIndexOf(".");
            String subKey = originalKey.substring(lastIndexOf + 1);
            if (CONVERSION_PATTERN.equalsIgnoreCase(subKey)) {
                LogLog.warn(String.format("log4j conf changed: Key=%s, value=%s >>> %s",
                        originalKey, propertyValue, componentProperties.getProperty(FILE_CONVERSION_PATTERN)));
                props.setProperty(originalKey, componentProperties.getProperty(FILE_CONVERSION_PATTERN));
            }
            if (!fileLogEnabled && (propertyValue.contains(SMART_APPENDER) || propertyValue.contains(ROLLING_FILE_APPENDER)
                    || propertyValue.contains(DAILY_ROLLING_FILE_APPENDER))) {
                props.remove(originalKey);
                LogLog.warn(String.format("log4j remove smart log appender: Key=%s", originalKey));
            }
        }
        if (props.toString().contains(SPINUS_LOGX_APPENDER)) {
            return;
        }
        String rootLoggerValue = props.getProperty(ROOT_LOGGER);
        props.setProperty(ROOT_LOGGER, rootLoggerValue + "," + "spinus");
        LogLog.warn(String.format("log4j conf changed: Key=%s, value=%s >>> %s",
                ROOT_LOGGER, rootLoggerValue, rootLoggerValue + "," + "spinus"));

        props.setProperty(LOG4J_APPENDER_SPINUS, SpinusLogxAppender.class.getName());
        LogLog.warn(String.format("log4j add log appender: Key=%s >>> value=%s",
                LOG4J_APPENDER_SPINUS, SpinusLogxAppender.class.getName()));
    }

    private Properties getComponentProperties() {
        try {
            Properties properties = new Properties();
            URL url = new URL(getClass().getResource("/log4j.component.properties").toString());
            properties.load(url.openStream());
            return properties;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
